use std::hash::Hash;

/// 此处定义一个distinct的迭代trait
pub trait UniqueIterator: Iterator {
    fn distinct(self) -> UniqueIteratorIpml<Self::Item, Self, impl Fn(&Self::Item) -> Self::Item>
    where
        Self: Sized,
        Self::Item: Eq + Hash + Clone,
    {
        UniqueIteratorIpml::new(self, |item| item.clone())
    }

    fn distinct_by<K, F>(self, item_getter: F) -> UniqueIteratorIpml<K, Self, F>
    where
        Self: Sized,
        K: Eq + Hash + Clone,
        F: Fn(&Self::Item) -> K,
    {
        UniqueIteratorIpml::new(self, item_getter)
    }
}

/// 此处实现一个distinct迭代类
pub struct UniqueIteratorIpml<K, I, F>
where
    I: Iterator,
    K: Eq + Hash + Clone,
    F: Fn(&I::Item) -> K,
{
    itr: I,
    distincter: std::collections::HashSet<K>,
    item_getter: F,
}

impl<K, I, F> UniqueIteratorIpml<K, I, F>
where
    I: Iterator,
    K: Eq + Hash + Clone,
    F: Fn(&I::Item) -> K,
{
    pub fn new(itr: I, item_getter: F) -> UniqueIteratorIpml<K, I, F> {
        UniqueIteratorIpml {
            itr,
            distincter: std::collections::HashSet::new(),
            item_getter,
        }
    }
}

/// 给迭代类赋予接上Iterator trait功能
///  
impl<K, I, F> Iterator for UniqueIteratorIpml<K, I, F>
where
    I: Iterator,
    K: Eq + Hash + Clone,
    F: Fn(&I::Item) -> K,
{
    type Item = I::Item;

    fn next(&mut self) -> Option<Self::Item> {
        self.itr
            .find(|item| self.distincter.insert((self.item_getter)(item)))
    }
}

/// 给全局的Iterator trait类赋予 UniqueIterate功能
///Xx
impl<T: Iterator> UniqueIterator for T {}
#[test]
pub fn test_iter_ext() {
    use itertools::*;
    use std::collections::HashMap;
    let vec = vec![1, 2, 3, 3];
    println!("{:?}", vec);

    let vec_distinct = vec
        .iter()
        // .distinct_by(|e| {})
        .distinct_by(|e| *e + 1)
        .collect::<Vec<_>>();

    let map = vec
        .iter()
        .group_by(|e| *e)
        .into_iter()
        .map(|(&key, group)| (key, group.into_iter().cloned().collect::<Vec<_>>()))
        .collect::<HashMap<_, _>>();
    // //  map(|g|).collect::<HashMap<_,_>>();
    println!("{:?}", vec_distinct);
    println!("{:?}", map);
}
